<template>
    <div class="message-list" id="message-list" ref="messageListContent">
        <div class="message-list-content">
            <div class="msg-more">
                <span class="load-more" v-if="chat.detail.more && !loading && !error"
                    @click="getMoreDetails">查看和ta的历史消息</span>
                <span class="no-more" v-if="!chat.detail.more">没有更多消息了</span>
                <span class="loading" v-if="loading">
                    <MessageLoading></MessageLoading>
                </span>
                <span class="error" v-if="error">消息加载失败，<span class="btn" @click="getMoreDetails">点击重新加载</span></span>
            </div>
            <div v-for="(item, index) in chat.detail.list" :key="index">
                <div class="msg-time" v-if="index === 0 || timeDiff(chat.detail.list[index - 1].time, item.time)">
                    <span class="time">{{ handleDateTime(item.time) }}</span>
                </div>
                <div class="msg-item" v-if="item.withdraw !== 1" :class="item.userId === user.uid ? 'is-me' : 'not-me'">
                    <a :href="`/space/${item.userId}`" target="_blank" class="avatar"
                        :style="`background-image: url('${item.userId === user.uid ? user.avatar : chat.user.avatar}');`"></a>
                    <div class="message" @contextmenu="(e) => handleContextMenu(item.id, e)">
                        <div class="message-content" v-html="emojiText(item.content)"></div>
                        <div class="context-menu" v-if="msgId === item.id"
                            :style="`left: ${menuLeft}px; top: ${menuTop}px;`">
                            <ul>
                                <li v-if="item.userId === user.uid" @click="withdraw">撤回</li>
                                <li @click="deleteMsg">删除</li>
                            </ul>
                        </div>
                    </div>
                </div>
                <div class="notify-wrapper" v-else>
                    <span class="notify-text">{{ item.userId === user.uid ? '你' : '对方' }}撤回了一条消息</span>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount, nextTick, computed } from 'vue';
import { useUserStore } from '@/store/user';
import MessageLoading from './MessageLoading.vue';
import { handleDateTime, emojiText } from '@/utils/utils.js';
import { getMoreChatDetails, deleteChatDetail } from '@/api/chatdetail';

const userStore = useUserStore();
const messageListContent = ref(null);

// props
const props = defineProps({
    mid: {
        type: Number,
        default: 0
    }
});

// 数据
const chat = ref({
    chat: {},
    detail: {
        list: [],
        more: true,
    },
    user: {}
});
const loading = ref(false);
const error = ref(false);
const isAtBottom = ref(true);
const msgId = ref(-1);
const menuLeft = ref(0);
const menuTop = ref(0);

// 计算属性
const user = computed(() => userStore.user);

// 请求更多聊天记录
const getMoreDetails = async () => {
    if (!chat.value.detail.more || loading.value) return;
    loading.value = true;

    const res = await getMoreChatDetails(props.mid, chat.value.detail.list.length)

    // 记录DOM更新前滚动条的位置
    const lastChild = messageListContent.value.lastElementChild;
    const lastTop = messageListContent.value.scrollTop;
    const lastHeight = lastChild.clientHeight;

    if (res.data) {
        let chatItem = userStore.chatList.find(item => item.user.uid === props.mid);
        chatItem.detail.more = res.data.more;
        chatItem.detail.list.unshift(...res.data.list);
    } else {
        loading.value = false;
        error.value = true;
    }

    nextTick(() => {
        loading.value = false;
        nextTick(() => {
            const currHeight = lastChild.clientHeight;
            const currTop = currHeight - lastHeight + lastTop;
            messageListContent.value.scrollTo({
                behavior: "auto",
                top: currTop
            });
        });
    });
};


// 判断两个时间是否相差超过4分钟
const timeDiff = (small, big) => {
    const smallTime = new Date(small).getTime();
    const bigTime = new Date(big).getTime();
    const timeDifferenceInMinutes = Math.floor((bigTime - smallTime) / (60 * 1000));
    return timeDifferenceInMinutes > 4;
};

// 滚动触底和触顶
const handleScroll = async () => {
    const isAtBottomValue = messageListContent.value.scrollHeight - messageListContent.value.scrollTop <= messageListContent.value.clientHeight + 10;
    const isNearTop = messageListContent.value.scrollTop <= 80;

    isAtBottom.value = isAtBottomValue;
    if (isNearTop && chat.value.detail.more) {
        await getMoreDetails();
    }
};

// 等元素渲染完再滚
const goBottom = (behavior) => {
    nextTick(() => {
        const lastChild = messageListContent.value.lastElementChild;
        messageListContent.value.scrollTo({
            behavior: behavior,
            top: lastChild.clientHeight - messageListContent.value.clientHeight
        });
        isAtBottom.value = true;
    });
};

// 右键消息菜单
const handleContextMenu = (msgId, e) => {
    e.preventDefault();
    menuLeft.value = e.clientX;
    menuTop.value = e.clientY;
    msgId.value = msgId;
};

// 点击空白处关闭菜单
const handleOutsideClick = (event) => {
    const msgList = document.querySelectorAll(".message");
    for (let i = 0; i < msgList.length; i++) {
        const element = msgList[i];
        if (element.contains(event.target)) return;
    }
    msgId.value = -1;
};

// 撤回消息
const withdraw = () => {
    const context = {
        code: 102,
        id: msgId.value
    };
    userStore.ws.send(JSON.stringify(context));
    msgId.value = -1;
};

// 删除消息
const deleteMsg = async () => {

    const res = await deleteChatDetail(msgId.value)

    if (res.code === 1) {
        let chatItem = userStore.chatList.find(item => item.user.uid === props.mid);
        let index = chatItem.detail.list.findIndex(item => item.id);
        if (index !== -1) {
            chatItem.detail.list.splice(index, 1);
            msgId.value = -1;
        }
    }
};

// 监听 props 和 store 变化
watch(() => props.mid, (curr) => {
    let chatItem = userStore.chatList.find(item => item.user.uid === curr);
    chat.value = { ...chatItem };
    goBottom("auto");
});

watch(() => userStore.chatList, (curr) => {
    nextTick(() => {
        const currChat = curr.find(item => item.user.uid === props.mid);
        chat.value = { ...currChat };
        if (isAtBottom.value) {
            goBottom("smooth");
        }
    });
}, { deep: true });

// 挂载
onMounted(() => {
    messageListContent.value.addEventListener('scroll', handleScroll);
    goBottom("auto");
    window.addEventListener("click", handleOutsideClick);
    window.addEventListener("contextmenu", handleOutsideClick);
});

// 卸载
onBeforeUnmount(() => {
    window.removeEventListener("click", handleOutsideClick);
    window.removeEventListener("contextmenu", handleOutsideClick);
});

</script>

<style scoped>
.message-list {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    position: relative;
    overflow-x: hidden;
    overflow-y: scroll;
    background-color: #f4f5f7;
}

.message-list::-webkit-scrollbar {
    width: 8px;
    height: 8px;
    background-color: rgba(1, 1, 1, 0);
}

.message-list::-webkit-scrollbar-thumb {
    background: #aaa;
    border-radius: 4px;
}

.message-list-content {
    overflow: auto;
    min-height: 100% !important;
}

.msg-more {
    padding: 16px 0 0;
    text-align: center;
    min-height: 38px;
}

.load-more {
    color: #ff91b9;
    cursor: pointer;
}

.no-more,
.error {
    color: #999;
}

.error .btn {
    color: #e36f90;
    cursor: pointer;
}

.msg-time {
    padding: 16px 0 16px;
    text-align: center;
}

.msg-time .time {
    color: #999;
    font-size: 12px;
    line-height: 22px;
    margin: 0 10px;
}

.msg-item {
    min-height: 48px;
    padding: 0 16px 16px;
    overflow: hidden;
    position: relative;
}

.avatar {
    display: block;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    -ms-flex-negative: 0;
    flex-shrink: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-image: url('https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png');
    float: left;
}

.is-me .avatar {
    float: right;
}

.message {
    float: left;
    max-width: 480px;
    margin: 0 10px;
    position: relative;
    overflow: hidden;
    color: #333;
}

.is-me .message {
    position: relative;
    float: right;
    color: #fff;
}

.message-content {
    line-height: 1.5;
    font-size: 14px;
    min-height: 37px;
    padding: 8px 16px;
    word-wrap: break-word;
    word-break: break-word;
    border-radius: 0 16px 16px 16px;
    overflow: hidden;
    background: #fff;
}

.is-me .message-content {
    background: #ffa7c2;
    border-radius: 16px 0 16px 16px;
}

.message-content:not(.is-img) {
    position: relative;
    z-index: 1;
}

.context-menu {
    position: fixed;
    width: 120px;
    z-index: 12;
    background: #eeeff1;
    color: #6c6c6c;
    border-radius: 6px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-box-shadow: 0 2px 4px 0 rgb(167, 111, 132, 33%);
    box-shadow: 0 2px 4px 0 rgb(167, 111, 132, 33%);
    font-size: 13px;
    line-height: 1.5;
    overflow: visible;
}

.context-menu ul {
    margin: 0;
    padding: 0;
    list-style: none;
    border-radius: 6px;
    overflow: hidden;
}

.context-menu ul li {
    padding: 3px 12px 3px 12px;
    cursor: pointer;
}

.context-menu ul li:first-child {
    padding-top: 6px;
}

.context-menu ul li:last-child {
    padding-bottom: 6px;
}

.context-menu ul li:hover {
    background: rgba(0, 0, 0, 0.05);
}

.notify-wrapper {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    padding: 0 10px;
    color: #b2b2b2;
    font-size: 12px;
    line-height: 16px;
    min-height: 30px;
}

.notify-wrapper .notify-text {
    padding: 2px 20px;
    background-color: #eaeaea;
    border-radius: 10px;
    line-height: 16px;
    height: 20px;
}
</style>